package reflect

import (
	"errors"
	"reflect"
)

// IterateFields 遍历字段
func IterateFields(entity any) (map[string]any, error) { // 作者习惯: 公共方法都有一个error返回值
	if entity == nil {
		return nil, errors.New("不支持 nil")
	}
	typ := reflect.TypeOf(entity)
	val := reflect.ValueOf(entity)
	if val.IsZero() {
		// 零值取Field会报错
		return nil, errors.New("不支持零值")
	}

	// 如果不是结构体
	//if typ.Kind() != reflect.Struct {
	// 指针
	// for是为了防止多级指针
	for typ.Kind() == reflect.Pointer {
		typ = typ.Elem()
		//  val.Field(i) 的val必须是struct
		val = val.Elem()
	}

	if typ.Kind() != reflect.Struct {
		return nil, errors.New("不支持类型")
	}
	// struct有多少字段
	numFields := typ.NumField()
	res := make(map[string]any, numFields)
	for i := 0; i < numFields; i++ {
		// 字段类型
		fieldType := typ.Field(i)
		// 字段值
		fieldVal := val.Field(i)
		// public的字段才能被反射获取
		if fieldType.IsExported() {
			// 反射获取的值被封装为Value,.Interface()就是获取Value里真正意义上的值
			res[fieldType.Name] = fieldVal.Interface()
		} else {
			// 获取不到的值就给零值
			res[fieldType.Name] = reflect.Zero(fieldType.Type).Interface()
		}
	}

	return res, nil
}

func SetField(entity any, field string, newValue any) error {
	val := reflect.ValueOf(entity)
	// 拿到指针指向的结构体
	for val.Type().Kind() == reflect.Pointer {
		val = val.Elem()
	}

	fieldVal := val.FieldByName(field)
	// 能否被修改
	if !fieldVal.CanSet() {
		return errors.New("不可修改字段")
	}
	fieldVal.Set(reflect.ValueOf(newValue))

	return nil
}
